﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

// 05/05/08 David Amenta - DaveAmenta.com
// 14/09/11 - Edited to add 64 bit systems support

namespace DeleteToRecycleBin
{
    /// <summary>
    /// Send files directly to the recycle bin.
    /// </summary>
    public class RecybleBin
    {

        /// <summary>
        /// Possible flags for the SHFileOperation method.
        /// </summary>
        [Flags]
        public enum FileOperationFlags : ushort
        {
            /// <summary>
            /// Do not show a dialog during the process
            /// </summary>
            FOF_SILENT = 0x0004,
            /// <summary>
            /// Do not ask the user to confirm selection
            /// </summary>
            FOF_NOCONFIRMATION = 0x0010,
            /// <summary>
            /// Delete the file to the recycle bin.  (Required flag to send a file to the bin
            /// </summary>
            FOF_ALLOWUNDO = 0x0040,
            /// <summary>
            /// Do not show the names of the files or folders that are being recycled.
            /// </summary>
            FOF_SIMPLEPROGRESS = 0x0100,
            /// <summary>
            /// Surpress errors, if any occur during the process.
            /// </summary>
            FOF_NOERRORUI = 0x0400,
            /// <summary>
            /// Warn if files are too big to fit in the recycle bin and will need
            /// to be deleted completely.
            /// </summary>
            FOF_WANTNUKEWARNING = 0x4000,
        }

        /// <summary>
        /// File Operation Function Type for SHFileOperation
        /// </summary>
        public enum FileOperationType : uint
        {
            /// <summary>
            /// Move the objects
            /// </summary>
            FO_MOVE = 0x0001,
            /// <summary>
            /// Copy the objects
            /// </summary>
            FO_COPY = 0x0002,
            /// <summary>
            /// Delete (or recycle) the objects
            /// </summary>
            FO_DELETE = 0x0003,
            /// <summary>
            /// Rename the object(s)
            /// </summary>
            FO_RENAME = 0x0004,
        }

        #region SHFILEOPSTRUCT

        public struct SHFILEOPSTRUCT
        {
            public IntPtr hwnd;
            public UInt32 wFunc;
            public IntPtr pFrom;
            public IntPtr pTo;
            public UInt16 fFlags;
            public Int32 fAnyOperationsAborted;
            public IntPtr hNameMappings;
            [MarshalAs(UnmanagedType.LPWStr)]
            public String lpszProgressTitle;

            public void CopyFrom(SHFILEOPSTRUCT32 initializer)
            {
                this.hwnd = initializer.hwnd;
                this.wFunc = initializer.wFunc;
                this.pFrom = initializer.pFrom;
                this.pTo = initializer.pTo;
                this.fFlags = initializer.fFlags;
                this.fAnyOperationsAborted = initializer.fAnyOperationsAborted;
                this.hNameMappings = initializer.hNameMappings;
                this.lpszProgressTitle = initializer.lpszProgressTitle;
            }

            public void CopyFrom(SHFILEOPSTRUCT64 initializer)
            {
                this.hwnd = initializer.hwnd;
                this.wFunc = initializer.wFunc;
                this.pFrom = initializer.pFrom;
                this.pTo = initializer.pTo;
                this.fFlags = initializer.fFlags;
                this.fAnyOperationsAborted = initializer.fAnyOperationsAborted;
                this.hNameMappings = initializer.hNameMappings;
                this.lpszProgressTitle = initializer.lpszProgressTitle;
            }
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 2)]
        public struct SHFILEOPSTRUCT32
        {
            public IntPtr hwnd;
            public UInt32 wFunc;
            public IntPtr pFrom;
            public IntPtr pTo;
            public UInt16 fFlags;
            public Int32 fAnyOperationsAborted;
            public IntPtr hNameMappings;
            [MarshalAs(UnmanagedType.LPWStr)]
            public String lpszProgressTitle;

            public SHFILEOPSTRUCT32(SHFILEOPSTRUCT initializer)
            {
                this.hwnd = initializer.hwnd;
                this.wFunc = initializer.wFunc;
                this.pFrom = initializer.pFrom;
                this.pTo = initializer.pTo;
                this.fFlags = initializer.fFlags;
                this.fAnyOperationsAborted = initializer.fAnyOperationsAborted;
                this.hNameMappings = initializer.hNameMappings;
                this.lpszProgressTitle = initializer.lpszProgressTitle;
            }
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 8)]
        public struct SHFILEOPSTRUCT64
        {
            public IntPtr hwnd;
            public UInt32 wFunc;
            public IntPtr pFrom;
            public IntPtr pTo;
            public UInt16 fFlags;
            public Int32 fAnyOperationsAborted;
            public IntPtr hNameMappings;
            [MarshalAs(UnmanagedType.LPWStr)]
            public String lpszProgressTitle;

            public SHFILEOPSTRUCT64(SHFILEOPSTRUCT initializer)
            {
                this.hwnd = initializer.hwnd;
                this.wFunc = initializer.wFunc;
                this.pFrom = initializer.pFrom;
                this.pTo = initializer.pTo;
                this.fFlags = initializer.fFlags;
                this.fAnyOperationsAborted = initializer.fAnyOperationsAborted;
                this.hNameMappings = initializer.hNameMappings;
                this.lpszProgressTitle = initializer.lpszProgressTitle;
            }
        }

        // To deal with 32 vs 64 bit issues we need two methods here.
        [DllImport("shell32.dll", CharSet = CharSet.Unicode, EntryPoint = "SHFileOperation")]
        public static extern Int32 SHFileOperation32(
            ref SHFILEOPSTRUCT32 lpFileOp);

        [DllImport("shell32.dll", CharSet = CharSet.Unicode, EntryPoint = "SHFileOperation")]
        public static extern Int32 SHFileOperation64(
            ref SHFILEOPSTRUCT64 lpFileOp);


        public static Int32 SHFileOperation(ref SHFILEOPSTRUCT lpFileOp)
        {
            Int32 result;

            switch (IntPtr.Size)
            {
                // 32 bit system
                case 4:
                    SHFILEOPSTRUCT32 fos32 = new SHFILEOPSTRUCT32(lpFileOp);
                    result = SHFileOperation32(ref fos32);
                    lpFileOp.CopyFrom(fos32);
                    break;

                // 64 bit system
                case 8:
                    SHFILEOPSTRUCT64 fos64 = new SHFILEOPSTRUCT64(lpFileOp);
                    result = SHFileOperation64(ref fos64);
                    lpFileOp.CopyFrom(fos64);
                    break;

                default:
                    throw new ArgumentException("Platform not supported");
            }

            return result;
        }

        #endregion SHFILEOPSTRUCT

        /// <summary>
        /// Send file to recycle bin
        /// </summary>
        /// <param name="path">Location of directory or file to recycle</param>
        /// <param name="flags">FileOperationFlags to add in addition to FOF_ALLOWUNDO</param>
        public static bool Send(string path, FileOperationFlags flags)
        {
            try
            {
                SHFILEOPSTRUCT fs = new SHFILEOPSTRUCT();
                fs.wFunc = (uint)FileOperationType.FO_DELETE;

                // important to double-terminate the string.
                fs.pFrom = Marshal.StringToHGlobalUni(path + '\0' + '\0');
                fs.fFlags = (ushort)(FileOperationFlags.FOF_ALLOWUNDO | flags);
                SHFileOperation(ref fs);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Send file to recycle bin.  Display dialog, display warning if files are too big to fit (FOF_WANTNUKEWARNING)
        /// </summary>
        /// <param name="path">Location of directory or file to recycle</param>
        public static bool Send(string path)
        {
            return Send(path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_WANTNUKEWARNING);
        }

        /// <summary>
        /// Send file silently to recycle bin.  Surpress dialog, surpress errors, delete if too large.
        /// </summary>
        /// <param name="path">Location of directory or file to recycle</param>
        public static bool SendSilent(string path)
        {
            return Send(path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_NOERRORUI | FileOperationFlags.FOF_SILENT);
        }
    }
}
